package com.gitee.loyo.mybill;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.codec.StringDecoder;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

public class Application {

    public static void main(String[] args) {
        var wechatBillFiles = new File[]{
          new File("D:\\Download\\微信支付账单(20210101-20210401)\\微信支付账单(20210101-20210401).csv"),
          new File("D:\\Download\\微信支付账单(20210401-20210701)\\微信支付账单(20210401-20210701).csv"),
          new File("D:\\Download\\微信支付账单(20210701-20211001)\\微信支付账单(20210701-20211001).csv"),
          new File("D:\\Download\\微信支付账单(20211001-20220101)\\微信支付账单(20211001-20220101).csv"),
        };

        var alipayBillFiles = new File[]{
          new File("D:\\Download\\alipay_record_20220128_1702_1.csv")
        };

        var bills = new ArrayList<Bill>();
        for (File wechatBillFile : wechatBillFiles) {
            var wechatBills = readWechatBill(wechatBillFile);
            for (WechatBill wechatBill : wechatBills) {
                if("支出".equals(wechatBill.getIncomeOrExpenditure())) {
                    bills.add(wechatBill.toBill());
                }
            }
        }
        for (File alipayBillFile : alipayBillFiles) {
            var alipayBills = readAlipayBill(alipayBillFile);
            for (AlipayBill alipayBill : alipayBills) {
                if("支出".equals(alipayBill.getIncomeOrExpenditure())) {
                    bills.add(alipayBill.toBill());
                }
            }
        }

        var merchantListFile = new File("D:\\Download\\商户.properties");
//        writeMerchantListFile(merchantListFile, bills);
//
//        writeBillList(new File("D:\\Download\\清单.xlsx"), bills);
        writeBillRank(merchantListFile, new File("D:\\Download\\统计.xlsx"), bills);
    }

    /**
     * 写入商户清单
     */
    @SneakyThrows
    static void writeMerchantListFile(File file, List<Bill> bills){
        var merchants = new LinkedHashMap<>();
        if(file.exists()){
            merchants.putAll(Files.readAllLines(file.toPath()).stream()
                .map(s -> s.split("="))
                .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1])));
        }

        bills.stream().map(Bill::getMerchant).distinct()
            .map(String::trim)
            .filter(s -> !merchants.containsKey(s))
            .forEachOrdered(s -> merchants.put(s, s));
        var bytes = merchants.entrySet().stream().map(entry ->
            entry.getKey() + "=" + entry.getValue()
        ).collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8);
        Files.write(file.toPath(), bytes);
    }

    /**
     * 写入账单清单
     */
    @SneakyThrows
    static void writeBillList(File billListFile, List<Bill> bills){
        EasyExcel.write(billListFile).build().write(bills, new WriteSheet()).finish();
    }

    /**
     * 写入账单清单
     */
    @SneakyThrows
    static void writeBillRank(File merchantListFile, File billRankFile, List<Bill> bills){
        var merchants = Files.readAllLines(merchantListFile.toPath()).stream()
            .map(s -> s.split("="))
            .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1]));

        var rankMap = new LinkedHashMap<String, MerchantPayment>();
        for (Bill bill : bills) {
            var mp = rankMap.computeIfAbsent(merchants.get(bill.merchant), s -> new MerchantPayment(merchants.get(bill.merchant), BigDecimal.ZERO));
            mp.amount = mp.amount.add(bill.amount);
        }
        var rankList = new ArrayList<>(rankMap.values());
        Collections.sort(rankList, (o1, o2) -> o2.amount.compareTo(o1.amount));
        EasyExcel.write(billRankFile).build().write(rankList, new WriteSheet()).finish();
    }

    @Data
    @AllArgsConstructor
    static class MerchantPayment{
        String merchant;
        BigDecimal amount;
    }

    /**
     * 读取微信账单
     */
    static List<WechatBill> readWechatBill(File file){
        var list = new ArrayList<WechatBill>();
        EasyExcel.read(file, WechatBill.class, new ReadListener<WechatBill>() {

            @Override
            public void invoke(WechatBill wechatBill, AnalysisContext analysisContext) {
                list.add(wechatBill);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {

            }
        }).sheet().headRowNumber(17).doRead();
        return list;
    }

    @Data
    public static class WechatBill{
        String tradeTime;           //交易时间
        String tradeType;           //交易类型
        String merchant;            //交易对方
        String product;             //商品
        String incomeOrExpenditure; //收支
        String amount;              //金额
        String paymentType;         //支付方式
        String status;              //当前状态
        String tradeNo;             //交易单号
        String merchantNo;          //商户单号

        public Bill toBill() {
            return new Bill(
                tradeTime.trim().length() == 19 ? LocalDateTime.parse(tradeTime.trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                : LocalDateTime.parse(tradeTime.trim(), DateTimeFormatter.ofPattern("yyyy/M/d H:m")),
                merchant,
                new BigDecimal(amount.replace("¥", "").replace(",", ""))
            );
        }
    }

    /**
     * 读取支付宝账单
     */
    @SneakyThrows
    static List<AlipayBill> readAlipayBill(File file){
        var list = new ArrayList<AlipayBill>();
        var fields = AlipayBill.class.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
        }
        try(var reader = new BufferedReader(new FileReader(file, Charset.forName("gb2312")))){
            for (int i = 0; i < 5; i++) {
                reader.readLine();
            }
            var records = CSVFormat.DEFAULT.parse(reader);
            for (CSVRecord record : records) {
                if(record.get(0) == null || record.get(0).contains("----")){
                    break;
                }
                var alipayBill = new AlipayBill();
                for (int i = 0; i < fields.length; i++) {
                    var value = record.get(i).trim();
                    fields[i].set(alipayBill, value);
                }
                list.add(alipayBill);
            }
        }
        return list;
    }

    @Data
    public static class AlipayBill{
        String tradeNo;             //交易号
        String merchantNo;          //商户订单号
        String tradeTime;           //交易创建时间
        String paymentTime;         //付款时间
        String latestUpdateTime;    //最近修改时间
        String tradeSource;         //交易来源
        String tradeType;           //类型
        String merchant;            //交易对方
        String product;             //商品名称
        String amount;              //金额
        String incomeOrExpenditure; //收支
        String paymentType;         //支付方式
        String status;              //交易状态

        public Bill toBill() {
            return new Bill(
                tradeTime.trim().length() == 19 ? LocalDateTime.parse(tradeTime.trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                    : LocalDateTime.parse(tradeTime.trim(), DateTimeFormatter.ofPattern("yyyy/M/d H:m")),
                merchant,
                new BigDecimal(amount.replace(",", ""))
            );
        }
    }

    @Data
    @AllArgsConstructor
    static class Bill{
        LocalDateTime tradeTime;
        String merchant;
        BigDecimal amount;
    }
}
